package vip.liux.contracts.security.externalLogin;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * 外部登录认证过滤器，对请求参数做严格校验和安全防护。
 */
@Setter
public class ExternalLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public static final String DEFAULT_LOGIN_PROVIDER_PARAM = "loginProvider";
    public static final String DEFAULT_PROVIDER_KEY_PARAM = "providerKey";
    private static final Logger log = LoggerFactory.getLogger(ExternalLoginAuthenticationFilter.class);
    private String loginProviderParameter = DEFAULT_LOGIN_PROVIDER_PARAM;
    private String providerKeyParameter = DEFAULT_PROVIDER_KEY_PARAM;

    public ExternalLoginAuthenticationFilter() {
        super("/login/sms");
    }

    public ExternalLoginAuthenticationFilter(String loginProcessingUrl) {
        super(loginProcessingUrl);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String loginProvider = request.getParameter(loginProviderParameter);
        String providerKey = request.getParameter(providerKeyParameter);

        validateAuthParams(loginProvider, providerKey);

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(loginProvider, providerKey);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    private void validateAuthParams(String loginProvider, String providerKey) {
        if (!StringUtils.hasText(loginProvider) || !StringUtils.hasText(providerKey)) {
            log.warn("认证请求参数缺失: loginProvider={}, providerKey={}", loginProvider, providerKey);
            throw new AuthenticationServiceException("认证参数不能为空");
        }
        if (!loginProvider.matches("^[a-zA-Z0-9_]{1,32}$")) {
            throw new AuthenticationServiceException("loginProvider格式非法");
        }
        if (!providerKey.matches("^[\\w\\-@.]{1,128}$")) {
            throw new AuthenticationServiceException("providerKey格式非法");
        }
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("外部认证成功: loginProvider={}, principal={}", loginProviderParameter, authResult.getPrincipal());
        super.successfulAuthentication(request, response, chain, authResult);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        log.warn("外部认证失败: {}", failed.getMessage());
        super.unsuccessfulAuthentication(request, response, failed);
    }
}